package github.ziwukun.jsonql.lang;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;

/**
 * 查询条件符号，例如`@and` `@or` `@gt`等<br>
 * JsonQL中形如：<br>
 * `@eq`，`@in`，`@and`，`@or`等等
 * 
 * @author Hugo.Yu
 */
public enum CmpOpr implements AtWord {
	/**
	 * AND条件
	 */
	AND("@and") {
		@Override
		Serializable getVal(String json) {
			// 获取条件的值对象
			Serializable valueObject = getJsonVal(json);
			if(!(valueObject instanceof JSONObject) && !(valueObject instanceof JSONArray)) {
				throw new JsonQLFormatException("@and条件的值为JSON对象",
					"@and语句语法错误-" + json);
			}
			// 类型转换
			if(valueObject instanceof JSONObject) {
				valueObject = jsonObjToArray((JSONObject)valueObject);
			}
			JSONArray array = (JSONArray)valueObject;
			
			ArrayList<Where> qls = new ArrayList<Where>(array.size());
			for (Object obj : array) {
				if(!(obj instanceof JSONObject)) {
					throw new JsonQLFormatException("@and条件JSON对象value值应该是JSON对象",
						"@and语句语法错误-" + obj.toString());
				}
				JSONObject itemJson = (JSONObject)obj;
				qls.add(WhereJson.readJson(itemJson.toString()));
			}
			return qls;
		}
		
		@Override
		public boolean support(Object value) {
			if(!(value instanceof Collection)) {
				return false;
			}
			for (Object item : (Collection<?>)value) {
				if(!(item instanceof Where)) {
					return false;
				}
//				Where propWhere = (Where)item;
//				if(!propWhere.keyIsProp()) {
//					return false;
//				}
			}
			return true;
		}
	},
	/**
	 * OR条件
	 */
	OR("@or") {
		@Override
		Serializable getVal(String json) {
			// 获取条件的值对象
			Serializable valueObject = getJsonVal(json);
			if(!(valueObject instanceof JSONObject) && !(valueObject instanceof JSONArray)) {
				throw new JsonQLFormatException("@or条件的值为JSON对象",
					"@or语句语法错误-" + json);
			}
			// 类型转换
			if(valueObject instanceof JSONObject) {
				valueObject = jsonObjToArray((JSONObject)valueObject);
			}
			JSONArray array = (JSONArray)valueObject;
			
			ArrayList<Where> qls = new ArrayList<Where>(array.size());
			for (Object obj : array) {
				if(!(obj instanceof JSONObject)) {
					throw new JsonQLFormatException("@or条件JSON对象value值应该是JSON对象",
						"@or语句语法错误-" + obj.toString());
				}
				JSONObject itemJson = (JSONObject)obj;
				qls.add(WhereJson.readJson(itemJson.toString()));
			}
			return qls;
		}

		@Override
		public boolean support(Object value) {
			if(!(value instanceof Collection)) {
				return false;
			}
			for (Object item : (Collection<?>)value) {
				if(!(item instanceof Where)) {
					return false;
				}
//				Where propWhere = (Where)item;
//				if(!propWhere.keyIsProp()) {
//					return false;
//				}
			}
			return true;
		}
	},
	/**
	 * 大于查询<br>
	 * value值类型必须是：数值类型，时间类型<br>
	 */
	GT("@gt") {
		@Override
		Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isCompareable(this, valueObject);
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			return checkIsCompareable(value);
		}
	},
	/**
	 * 小于查询<br>
	 * value值类型必须是：数值类型，时间类型<br>
	 */
	LT("@lt") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isCompareable(this, valueObject);
			return valueObject;
		}
		
		@Override
		public boolean support(Object value) {
			return checkIsCompareable(value);
		}
	},
	/**
	 * 大于等于查询<br>
	 * value值类型必须是：数值类型，时间类型<br>
	 */
	GTE("@gte") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isCompareable(this, valueObject);
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			return checkIsCompareable(value);
		}
	},
	/**
	 * 小于等于查询<br>
	 * value值类型必须是：数值类型，时间类型<br>
	 */
	LTE("@lte") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isCompareable(this, valueObject);
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			return checkIsCompareable(value);
		}
	},
	/**
	 * 等于查询<br>
	 * value值类型必须是：数值类型，字符串类型<br>
	 */
	EQ("@eq") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isSimple(this, valueObject);
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			return checkIsSimple(value);
		}
	},
	/**
	 * 不等于查询<br>
	 * value值类型必须是：数值类型，字符串类型<br>
	 */
	NEQ("@neq") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			isSimple(this, valueObject);
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			return checkIsSimple(value);
		}
	},
	/**
	 * IN查询<br>
	 */
	IN("@in") {
		@Override
		Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			if (!(valueObject instanceof JSONArray)) {
				throw new JsonQLFormatException(this.atWord() + "的条件值必须是JSON ARRAY类型",
					"@in语句语法错误:@in的值类型为数组");
			}
			
			// 数组元素值必须全部是简单类型 并且将JSONArray转换成List
			JSONArray valueArray = (JSONArray) valueObject;
			ArrayList<Serializable> valueList = new ArrayList<Serializable>(valueArray.size());
			for (Iterator<Object> iterator = valueArray.iterator(); iterator.hasNext();) {
				Object itemObject = iterator.next();
				isSimple(this, itemObject);
				valueList.add((Serializable) itemObject);
			}
			
			return valueList;
		}

		@Override
		public boolean support(Object value) {
			if (value == null) {
				return false;
			}
			if (!Collection.class.isAssignableFrom(value.getClass())) {
				return false;
			}

			Collection<?> coll = (Collection<?>) value;
			for (Object item : coll) {
				if (!checkIsSimple(item)) {
					return false;
				}
			}
			return true;
		}
	},
	/**
	 * NOT IN查询<br>
	 */
	NIN("@nin") {
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			if (!(valueObject instanceof JSONArray)) {
				throw new JsonQLFormatException(this.atWord() + "的条件值必须是JSON ARRAY类型",
					"@nin语句语法错误:@nin的值类型为数组");
			}
			
			// 数组元素值必须全部是简单类型 并且将JSONArray转换成List
			JSONArray valueArray = (JSONArray) valueObject;
			ArrayList<Serializable> valueList = new ArrayList<Serializable>(valueArray.size());
			for (Iterator<Object> iterator = valueArray.iterator(); iterator.hasNext();) {
				Object itemObject = iterator.next();
				isSimple(this, itemObject);
				valueList.add((Serializable) itemObject);
			}
			
			return valueList;
		}

		@Override
		public boolean support(Object value) {
			if (value == null) {
				return false;
			}
			if (!Collection.class.isAssignableFrom(value.getClass())) {
				return false;
			}

			Collection<?> coll = (Collection<?>) value;
			for (Object item : coll) {
				if (!checkIsSimple(item)) {
					return false;
				}
			}
			return true;
		}
	},
	ISNULL("@isnull"){
		/**
		 * {"@isnull":true}
		 */
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			if(!(valueObject instanceof Boolean)) {
				throw new JsonQLFormatException(
					ISNULL.atWord() + "条件值类型只能是boolean类型",
					"@isnull语句语法错误:@isnull的值类型为boolean");
			}
			
			return valueObject;
		}

		@Override
		public boolean support(Object value) {
			if (value == null) {
				return false;
			}
			return value instanceof Boolean;
		}
	},
	NOTNULL("@notnull"){
		@Override
		 Serializable getVal(String json) {
			Serializable valueObject = getJsonVal(json);
			if(!(valueObject instanceof Boolean)) {
				throw new JsonQLFormatException(
					ISNULL.atWord() + "条件值类型只能是boolean类型",
					"@notnull语句语法错误:@notnull的值类型为boolean");
			}
			return valueObject;
		}
		@Override
		public boolean support(Object value) {
			if (value == null) {
				return false;
			}
			return value instanceof Boolean;
		}
	};
	
	private final String name;
	CmpOpr(String name) {
		this.name = name;
	}
	@Override
	public String atWord() {
		return this.name;
	}

	/**
	 * 转换JSON对象，并且将值对象转换成Java对象<br>
	 * JSON对象包含完成KV，形如<br>
	 * ```json {"@eq":"name"} ```
	 * @param json
	 * @return
	 */
	abstract Serializable getVal(String json);
	/**值是否能被条件允许*/
	public abstract boolean support(Object value);
	/**
	 * 统一的对JSON对象进行校验，查看是否符合条件SQL的约束条件<br>
	 * 并且将条件对象对应的值对象获取<br>
	 * 1 有且仅有一个key，并且key的值和枚举的name属性必须一致<br>
	 * 2 条件对象的值不能是null<br>
	 * 
	 * @param json
	 * @return
	 */
	private static Serializable getJsonVal(String json) {
		// 转换成JSON对象方便解析，并且获取必要数据
		// 必要的检测
		JSONObject jsonObject = new JSONObject(json);
		Set<String> keys = jsonObject.keySet();
		// key一定是枚举的name属性
		String key = keys.iterator().next();
		Object value = jsonObject.get(key);
		
		return (Serializable) value;
	}

	/**
	 * 检查JSON值对象是否是一个可以比较大小的值类型<br>
	 * 数值，数值型字符串，时间字符串等是可比较大小的值类型
	 * 
	 * @param json
	 */
	private static void isCompareable(CmpOpr cmpOpr, Object valueObject) {
		// 值类型只能是数值或时间字符串
		if (!(valueObject instanceof Number) && !(valueObject instanceof String)) {
			throw new JsonQLFormatException(cmpOpr.atWord() + "条件值类型只能是可比较类型JSON数据类型",
					cmpOpr.atWord() + "语句语法错误:VALUE值应该是可比较的数值、日期类型字符串、数值类型字符串");
		}
		// 如果值类型是字符串类型，那么需要符合时间类型或者能够转换成数字
		if (valueObject instanceof String) {
			String valueStr = (String) valueObject;
			if (!JsonValType.DATE.isType(valueStr) && !JsonValType.TIME.isType(valueStr)
					&& !JsonValType.NUMBER.isType(valueStr)) {
				throw new JsonQLFormatException(cmpOpr.atWord() + "条件值类型只能是可比较类型JSON字符串类型",
						cmpOpr.atWord() + "语句语法错误:VALUE值应该是可比较的数值、日期类型字符串、数值类型字符串");
			}
		}
	}

	/**
	 * 检查值是否是可以比较的值<br>
	 * 数值，数值型字符串，时间字符串等是可比较大小的值类型
	 */
	private static boolean checkIsCompareable(Object valueObject) {
		// 值类型只能是数值或时间字符串
		if (!(valueObject instanceof Number) && !(valueObject instanceof String)) {
			return false;
		}
		// 如果值类型是字符串类型，那么需要符合时间类型或者能够转换成数字
		if (valueObject instanceof String) {
			String valueStr = (String) valueObject;
			if (!JsonValType.DATE.isType(valueStr) && !JsonValType.TIME.isType(valueStr)
					&& !JsonValType.NUMBER.isType(valueStr)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 检查JSON值对象是一个简单的值对象，简单值对象包括<br>
	 * 数值，字符串
	 * 
	 * @param json
	 */
	private static void isSimple(CmpOpr cmpOpr, Object valueObject) {
		// 值类型只能是数值或时间字符串
		if (!(valueObject instanceof Number) && !(valueObject instanceof String) 
			&& !(valueObject instanceof Boolean)) {
			throw new JsonQLFormatException(
				cmpOpr.atWord() + "条件值类型只能是简单JSON数据类型",
				cmpOpr.atWord() + "语句语法错误:VALUE值应该是简单的数值或字符串类型");
		}
	}

	/**
	 * 检查JSON值对象是一个简单的值对象，简单值对象包括<br>
	 * 数值，字符串
	 * 
	 * @param json
	 */
	private static boolean checkIsSimple(Object valueObject) {
		// 值类型只能是数值或时间字符串
		if (!(valueObject instanceof Number) && !(valueObject instanceof String) && !(valueObject instanceof String)) {
			return false;
		}
		return true;
	}
	
	/**
	 * 提取出JSON的key值，转换成{key:value}对象<br>
	 * 组合成对象的列表
	 * @return
	 */
	private static JSONArray jsonObjToArray(JSONObject obj) {
		JSONArray array = new JSONArray();
		for (String key : obj.keySet()) {
			JSONObject itemJson = new JSONObject();
			itemJson.set(key, obj.get(key));
			array.add(itemJson);
		}
		
		return array;
	}

	/**
	 * 判断字符串是否是条件字符串</br>
	 * 
	 * @return
	 */
	public static boolean isCmpOpr(String key) {
		// 空字符串永远为false
		if (StrUtil.isBlank(key)) {
			return false;
		}

		// 去除@
		if (key.startsWith("@")) {
			key = key.substring(1, key.length());
		}

		return EnumUtil.contains(CmpOpr.class, key.toUpperCase());
	}

	/**
	 * 字符串转枚举，字符串包含@符号也可，忽略大小写
	 * 
	 * @param key
	 * @return
	 */
	public static CmpOpr valOf(String key) {
		// 空字符串永远为false
		if (StrUtil.isBlank(key)) {
			throw new RuntimeException("字符串转Condition枚举失败，字符串不能为空");
		}
		if (!key.startsWith("@")) {
			throw new RuntimeException("条件JSON必须以@字符开头");
		}
		// 去除@
		key = key.substring(1, key.length());

		return EnumUtil.fromString(CmpOpr.class, key.toUpperCase());
	}
}
